#pragma once

#include "Classifier.h"

#include "ClassifierTemplateParameter.h"
#include "CollaborationUse.h"
#include "Feature.h"
#include "Generalization.h"
#include "GeneralizationSet.h"
#include "Interface.h"
#include "NamedElement.h"
#include "Property.h"
#include "RedefinableTemplateSignature.h"
#include "StructuralFeature.h"
#include "Substitution.h"
#include "Type.h"
#include "UseCase.h"

namespace uml {

const bool Classifier::isAbstract_default_ = false;
const bool Classifier::isFinalSpecialization_default_ = false;
Classifier::Classifier(const QString& umlType)
    : Type(umlType) {}

void Classifier::fromJson(const QJsonObject& json, QStringList& errorList) {
    if (json.isEmpty()) {
        return;
    }

    Type::fromJson(json, errorList);
    RedefinableElement::fromJson(json, errorList);
    TemplateableElement::fromJson(json, errorList);
    Namespace::fromJson(json, errorList);
    JsonSerializable::fromJsonArray(attributes_, "attributes", json, errorList);
    JsonSerializable::fromJsonArray(collaborationUses_, "collaborationUses", json, errorList);
    JsonSerializable::fromJsonArray(features_, "features", json, errorList);
    JsonSerializable::fromJsonArray(generals_, "generals", json, errorList);
    JsonSerializable::fromJsonArray(generalizations_, "generalizations", json, errorList);
    JsonSerializable::fromJsonArray(inheritedMembers_, "inheritedMembers", json, errorList);
    isAbstract_ = json.value("isAbstract").toBool(isAbstract_default_);
    isFinalSpecialization_ = json.value("isFinalSpecialization").toBool(isFinalSpecialization_default_);
    ownedTemplateSignature_ = JsonSerializable::create<RedefinableTemplateSignature>(json.value("ownedTemplateSignature"), errorList);

    JsonSerializable::fromJsonArray(ownedUseCases_, "ownedUseCases", json, errorList);
    JsonSerializable::fromJsonArray(powertypeExtents_, "powertypeExtents", json, errorList);
    JsonSerializable::fromJsonArray(redefinedClassifiers_, "redefinedClassifiers", json, errorList);
    representation_ = JsonSerializable::create<CollaborationUse>(json.value("representation"), errorList);

    JsonSerializable::fromJsonArray(substitutions_, "substitutions", json, errorList);
    templateParameter_ = JsonSerializable::create<ClassifierTemplateParameter>(json.value("templateParameter"), errorList);

    JsonSerializable::fromJsonArray(useCases_, "useCases", json, errorList);
}

void Classifier::toJson(QJsonObject& json) {
    Type::toJson(json);
    RedefinableElement::toJson(json);
    TemplateableElement::toJson(json);
    Namespace::toJson(json);
    JsonSerializable::toJsonArray(attributes_, "attributes", json);
    JsonSerializable::toJsonArray(collaborationUses_, "collaborationUses", json);
    JsonSerializable::toJsonArray(features_, "features", json);
    JsonSerializable::toJsonArray(generals_, "generals", json);
    JsonSerializable::toJsonArray(generalizations_, "generalizations", json);
    JsonSerializable::toJsonArray(inheritedMembers_, "inheritedMembers", json);
    if (isAbstract_ != isAbstract_default_) {
        json.insert("isAbstract", isAbstract_);
    }
    if (isFinalSpecialization_ != isFinalSpecialization_default_) {
        json.insert("isFinalSpecialization", isFinalSpecialization_);
    }
    if (ownedTemplateSignature_) {
        QJsonObject ownedTemplateSignature_json;
        ownedTemplateSignature_->toJson(ownedTemplateSignature_json);
        json.insert("ownedTemplateSignature", ownedTemplateSignature_json);
    }
    JsonSerializable::toJsonArray(ownedUseCases_, "ownedUseCases", json);
    JsonSerializable::toJsonArray(powertypeExtents_, "powertypeExtents", json);
    JsonSerializable::toJsonArray(redefinedClassifiers_, "redefinedClassifiers", json);
    if (representation_) {
        QJsonObject representation_json;
        representation_->toJson(representation_json);
        json.insert("representation", representation_json);
    }
    JsonSerializable::toJsonArray(substitutions_, "substitutions", json);
    if (templateParameter_) {
        QJsonObject templateParameter_json;
        templateParameter_->toJson(templateParameter_json);
        json.insert("templateParameter", templateParameter_json);
    }
    JsonSerializable::toJsonArray(useCases_, "useCases", json);
}

/// <summary>
/// allFeatures
    /// The query allFeatures() gives all of the Features in the namespace of the
    /// Classifier. In general, through mechanisms such as inheritance, this will be a
    /// larger set than feature.
/// </summary>
    /// <code>
    /// result = (member->select(oclIsKindOf(Feature))->collect(oclAsType(Feature))->asSet())
    /// </code>
shared_ptr<vector<Feature>> Classifier::allFeatures() const
{
    return nullptr;
}

/// <summary>
/// allParents
    /// The query allParents() gives all of the direct and indirect ancestors of a
    /// generalized Classifier.
/// </summary>
    /// <code>
    /// result = (parents()->union(parents()->collect(allParents())->asSet()))
    /// </code>
shared_ptr<vector<Classifier>> Classifier::allParents() const
{
    return nullptr;
}

/// <summary>
/// conformsTo
    /// The query conformsTo() gives true for a Classifier that defines a type that
    /// conforms to another. This is used, for example, in the specification of
    /// signature conformance for operations.
/// </summary>
    /// <code>
    /// result = (if other.oclIsKindOf(Classifier) then
    ///   let otherClassifier : Classifier = other.oclAsType(Classifier) in
    ///     self = otherClassifier or allParents()->includes(otherClassifier)
    /// else
    ///   false
    /// endif)
    /// </code>
bool Classifier::conformsTo(shared_ptr<Type> other) const
{
    return false;
}

/// <summary>
/// hasVisibilityOf
    /// The query hasVisibilityOf() determines whether a NamedElement is visible in the
    /// classifier. Non-private members are visible. It is only called when the argument
    /// is something owned by a parent.
/// </summary>
    /// <code>
    /// allParents()->including(self)->collect(member)->includes(n)
    /// </code>
    /// <code>
    /// result = (n.visibility <> VisibilityKind::private)
    /// </code>
bool Classifier::hasVisibilityOf(shared_ptr<NamedElement> n) const
{
    return false;
}

/// <summary>
/// inherits
    /// The query inherit() defines how to inherit a set of elements passed as its
    /// argument.  It excludes redefined elements from the result.
/// </summary>
    /// <code>
    /// result = (inhs->reject(inh |
    ///   inh.oclIsKindOf(RedefinableElement) and
    ///   ownedMember->select(oclIsKindOf(RedefinableElement))->
    ///     select(redefinedElement->includes(inh.oclAsType(RedefinableElement)))
    ///        ->notEmpty()))
    /// </code>
shared_ptr<vector<NamedElement>> Classifier::inherits(vector<shared_ptr<NamedElement>> inhs) const
{
    return nullptr;
}

/// <summary>
/// inheritableMembers
    /// The query inheritableMembers() gives all of the members of a Classifier that may
    /// be inherited in one of its descendants, subject to whatever visibility
    /// restrictions apply.
/// </summary>
    /// <code>
    /// c.allParents()->includes(self)
    /// </code>
    /// <code>
    /// result = (member->select(m | c.hasVisibilityOf(m)))
    /// </code>
shared_ptr<vector<NamedElement>> Classifier::inheritableMembers(shared_ptr<Classifier> c) const
{
    return nullptr;
}

/// <summary>
/// isTemplate
    /// The query isTemplate() returns whether this Classifier is actually a template.
/// </summary>
    /// <code>
    /// result = (ownedTemplateSignature <> null or general->exists(g | g.isTemplate()))
    /// </code>
bool Classifier::isTemplate() const
{
    return false;
}

/// <summary>
/// maySpecializeType
    /// The query maySpecializeType() determines whether this classifier may have a
    /// generalization relationship to classifiers of the specified type. By default a
    /// classifier may specialize classifiers of the same or a more general type. It is
    /// intended to be redefined by classifiers that have different specialization
    /// constraints.
/// </summary>
    /// <code>
    /// result = (self.oclIsKindOf(c.oclType()))
    /// </code>
bool Classifier::maySpecializeType(shared_ptr<Classifier> c) const
{
    return false;
}

/// <summary>
/// parents
    /// The query parents() gives all of the immediate ancestors of a generalized
    /// Classifier.
/// </summary>
    /// <code>
    /// result = (generalization.general->asSet())
    /// </code>
shared_ptr<vector<Classifier>> Classifier::parents() const
{
    return nullptr;
}

/// <summary>
/// directlyRealizedInterfaces
    /// The Interfaces directly realized by this Classifier
/// </summary>
    /// <code>
    /// result = ((clientDependency->
    ///   select(oclIsKindOf(Realization) and supplier->forAll(oclIsKindOf(Interface))))->
    ///       collect(supplier.oclAsType(Interface))->asSet())
    /// </code>
shared_ptr<vector<Interface>> Classifier::directlyRealizedInterfaces() const
{
    return nullptr;
}

/// <summary>
/// directlyUsedInterfaces
    /// The Interfaces directly used by this Classifier
/// </summary>
    /// <code>
    /// result = ((supplierDependency->
    ///   select(oclIsKindOf(Usage) and client->forAll(oclIsKindOf(Interface))))->
    ///     collect(client.oclAsType(Interface))->asSet())
    /// </code>
shared_ptr<vector<Interface>> Classifier::directlyUsedInterfaces() const
{
    return nullptr;
}

/// <summary>
/// allRealizedInterfaces
    /// The Interfaces realized by this Classifier and all of its generalizations
/// </summary>
    /// <code>
    /// result = (directlyRealizedInterfaces()->union(self.allParents()->collect(directlyRealizedInterfaces()))->asSet())
    /// </code>
shared_ptr<vector<Interface>> Classifier::allRealizedInterfaces() const
{
    return nullptr;
}

/// <summary>
/// allUsedInterfaces
    /// The Interfaces used by this Classifier and all of its generalizations
/// </summary>
    /// <code>
    /// result = (directlyUsedInterfaces()->union(self.allParents()->collect(directlyUsedInterfaces()))->asSet())
    /// </code>
shared_ptr<vector<Interface>> Classifier::allUsedInterfaces() const
{
    return nullptr;
}

/// <summary>
/// isSubstitutableFor
/// </summary>
    /// <code>
    /// result = (substitution.contract->includes(contract))
    /// </code>
bool Classifier::isSubstitutableFor(shared_ptr<Classifier> contract) const
{
    return false;
}

/// <summary>
/// allAttributes
    /// The query allAttributes gives an ordered set of all owned and inherited
    /// attributes of the Classifier. All owned attributes appear before any inherited
    /// attributes, and the attributes inherited from any more specific parent
    /// Classifier appear before those of any more general parent Classifier. However,
    /// if the Classifier has multiple immediate parents, then the relative ordering of
    /// the sets of attributes from those parents is not defined.
/// </summary>
    /// <code>
    /// result = (attribute->asSequence()->union(parents()->asSequence().allAttributes())->select(p | member->includes(p))->asOrderedSet())
    /// </code>
shared_ptr<vector<Property>> Classifier::allAttributes() const
{
    return nullptr;
}

/// <summary>
/// allSlottableFeatures
    /// All StructuralFeatures related to the Classifier that may have Slots, including
    /// direct attributes, inherited attributes, private attributes in generalizations,
    /// and memberEnds of Associations, but excluding redefined StructuralFeatures.
/// </summary>
    /// <code>
    /// result = (member->select(oclIsKindOf(StructuralFeature))->
    ///   collect(oclAsType(StructuralFeature))->
    ///    union(self.inherit(self.allParents()->collect(p | p.attribute)->asSet())->
    ///      collect(oclAsType(StructuralFeature)))->asSet())
    /// </code>
shared_ptr<vector<StructuralFeature>> Classifier::allSlottableFeatures() const
{
    return nullptr;
}

/// <summary>
/// attributes getter
/// All of the Properties that are direct (i.e., not inherited or imported)
/// attributes of the Classifier.
/// </summary>
vector<shared_ptr<Property>>& Classifier::attributes() {
    return attributes_;
}

/// <summary>
/// collaborationUses getter
/// The CollaborationUses owned by the Classifier.
/// </summary>
vector<shared_ptr<CollaborationUse>>& Classifier::collaborationUses() {
    return collaborationUses_;
}

/// <summary>
/// features getter
/// Specifies each Feature directly defined in the classifier. Note that there may
/// be members of the Classifier that are of the type Feature but are not included,
/// e.g., inherited features.
/// </summary>
vector<shared_ptr<Feature>>& Classifier::features() {
    return features_;
}

/// <summary>
/// generals getter
/// The generalizing Classifiers for this Classifier.
/// </summary>
vector<shared_ptr<Classifier>>& Classifier::generals() {
    return generals_;
}

/// <summary>
/// generalizations getter
/// The Generalization relationships for this Classifier. These Generalizations
/// navigate to more general Classifiers in the generalization hierarchy.
/// </summary>
vector<shared_ptr<Generalization>>& Classifier::generalizations() {
    return generalizations_;
}

/// <summary>
/// inheritedMembers getter
/// All elements inherited by this Classifier from its general Classifiers.
/// </summary>
vector<shared_ptr<NamedElement>>& Classifier::inheritedMembers() {
    return inheritedMembers_;
}

/// <summary>
/// isAbstract getter
/// If true, the Classifier can only be instantiated by instantiating one of its
/// specializations. An abstract Classifier is intended to be used by other
/// Classifiers e.g., as the target of Associations or Generalizations.
/// </summary>
bool Classifier::isAbstract() const {
    return isAbstract_;
}

/// <summary>
/// isAbstract setter
/// If true, the Classifier can only be instantiated by instantiating one of its
/// specializations. An abstract Classifier is intended to be used by other
/// Classifiers e.g., as the target of Associations or Generalizations.
/// </summary>
void Classifier::setIsAbstract(bool value) {
    isAbstract_ = value;
}

/// <summary>
/// isFinalSpecialization getter
/// If true, the Classifier cannot be specialized.
/// </summary>
bool Classifier::isFinalSpecialization() const {
    return isFinalSpecialization_;
}

/// <summary>
/// isFinalSpecialization setter
/// If true, the Classifier cannot be specialized.
/// </summary>
void Classifier::setIsFinalSpecialization(bool value) {
    isFinalSpecialization_ = value;
}

/// <summary>
/// ownedTemplateSignature getter
/// The optional RedefinableTemplateSignature specifying the formal template
/// parameters.
/// </summary>
shared_ptr<RedefinableTemplateSignature> Classifier::ownedTemplateSignature() const {
    return ownedTemplateSignature_;
}

/// <summary>
/// ownedTemplateSignature setter
/// The optional RedefinableTemplateSignature specifying the formal template
/// parameters.
/// </summary>
void Classifier::setOwnedTemplateSignature(shared_ptr<RedefinableTemplateSignature> value) {
    ownedTemplateSignature_ = value;
}

/// <summary>
/// ownedUseCases getter
/// The UseCases owned by this classifier.
/// </summary>
vector<shared_ptr<UseCase>>& Classifier::ownedUseCases() {
    return ownedUseCases_;
}

/// <summary>
/// powertypeExtents getter
/// The GeneralizationSet of which this Classifier is a power type.
/// </summary>
vector<shared_ptr<GeneralizationSet>>& Classifier::powertypeExtents() {
    return powertypeExtents_;
}

/// <summary>
/// redefinedClassifiers getter
/// The Classifiers redefined by this Classifier.
/// </summary>
vector<shared_ptr<Classifier>>& Classifier::redefinedClassifiers() {
    return redefinedClassifiers_;
}

/// <summary>
/// representation getter
/// A CollaborationUse which indicates the Collaboration that represents this
/// Classifier.
/// </summary>
shared_ptr<CollaborationUse> Classifier::representation() const {
    return representation_;
}

/// <summary>
/// representation setter
/// A CollaborationUse which indicates the Collaboration that represents this
/// Classifier.
/// </summary>
void Classifier::setRepresentation(shared_ptr<CollaborationUse> value) {
    representation_ = value;
}

/// <summary>
/// substitutions getter
/// The Substitutions owned by this Classifier.
/// </summary>
vector<shared_ptr<Substitution>>& Classifier::substitutions() {
    return substitutions_;
}

/// <summary>
/// templateParameter getter
/// TheClassifierTemplateParameter that exposes this element as a formal parameter.
/// </summary>
shared_ptr<ClassifierTemplateParameter> Classifier::templateParameter() const {
    return templateParameter_;
}

/// <summary>
/// templateParameter setter
/// TheClassifierTemplateParameter that exposes this element as a formal parameter.
/// </summary>
void Classifier::setTemplateParameter(shared_ptr<ClassifierTemplateParameter> value) {
    templateParameter_ = value;
}

/// <summary>
/// useCases getter
/// The set of UseCases for which this Classifier is the subject.
/// </summary>
vector<shared_ptr<UseCase>>& Classifier::useCases() {
    return useCases_;
}

} // namespace uml
